import copy
import sys
import os

thisDir = os.path.split(os.path.realpath(__file__))[0]
sys.path.append(thisDir)

import PUConst

import logging
logger = logging.getLogger(__name__)

class PUUIGenerator:
    def __init__(self, generator):
        self._pu = generator

    def Generate(self, uid, ui):
        return self._pu.GenerateEmpty()

    def GenerateSubUI(self, uid, ui):
        subUIs = ui[PUConst.UiPropSubUI] if PUConst.UiPropSubUI in ui else []
        for ui in subUIs:
            yield self._pu.Generate(ui)

class PUPanelGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUTextGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUSeparatorGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUSpinPageGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PULayoutHorizontalGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PULayoutVerticalGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PULayoutGridGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUInputGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUPasswordGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUButtonGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PULinkGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUTabGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUTableGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUDatetimeGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUComboBoxGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUComboListGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUMapGenerator(PUUIGenerator):
    def __init__(self, generator):
        super().__init__(generator)

class PUGenerator:
    def __init__(self, uiConfig):
        self.__generatedId = 0
        self._uiConfig = self.__clone(uiConfig)
        self.__initUIGeneraters()

    def Configure(self, **kwargs):
        self._clear()
        for k, v in kwargs.items():
            self._dynamicConfig[k] = v

    def GetConfigure(self, key):
        return self._dynamicConfig[key] if key in self._dynamicConfig else None

    def Generate(self, ui):
        uid = None
        if isinstance(ui, str):
            uid = ui
            if uid not in self._uiConfig:
                logger.error(f"UID is not defined: {uid}")
                return self.GenerateEmpty()
            ui = self._uiConfig[uid]
        elif isinstance(ui, dict):
            if PUConst.UiPropID not in ui:
                logger.error(f"ID property is not defined in ui: {ui}")
                return self.GenerateEmpty()
            uid = ui[PUConst.UiPropID]
        else:
            logger.error(f"Unsupported UI: {ui}")
            return self.GenerateEmpty()
        generatedUI = self.GenerateEmpty()
        if uid == PUConst.UiBody:
            generatedUI += self._preGenerate()
        generatedUI += self.__getUIGenerator(ui[PUConst.UiPropType]).Generate(uid, ui)
        if uid == PUConst.UiBody:
            generatedUI += self._postGenerate()
        return generatedUI

    def GenerateEmpty(self):
        return ""

    ### Generators ###

    def _preGenerate(self):
        return self.GenerateEmpty()

    def _postGenerate(self):
        return self.GenerateEmpty()

    def _createPanelGenerator(self):
        return PUPanelGenerator(self)

    def _createTextGenerator(self):
        return PUTextGenerator(self)

    def _createSeparatorGenerator(self):
        return PUSeparatorGenerator(self)

    def _createSpinPageGenerator(self):
        return PUSpinPageGenerator(self)

    def _createLayoutHorizontalGenerator(self):
        return PULayoutHorizontalGenerator(self)

    def _createLayoutVerticalGenerator(self):
        return PULayoutVerticalGenerator(self)

    def _createLayoutGridGenerator(self):
        return PULayoutGridGenerator(self)

    def _createInputGenerator(self):
        return PUInputGenerator(self)

    def _createPasswordGenerator(self):
        return PUPasswordGenerator(self)

    def _createButtonGenerator(self):
        return PUButtonGenerator(self)

    def _createLinkGenerator(self):
        return PULinkGenerator(self)

    def _createTabGenerator(self):
        return PUTabGenerator(self)

    def _createTableGenerator(self):
        return PUTableGenerator(self)

    def _createDatetimeGenerator(self):
        return PUDatetimeGenerator(self)

    def _createComboBoxGenerator(self):
        return PUComboBoxGenerator(self)

    def _createComboListGenerator(self):
        return PUComboListGenerator(self)

    def _createMapGenerator(self):
        return PUMapGenerator(self)

    ### Protected ###

    def _clear(self):
        self._dynamicConfig = {}

    ### Private ###

    def __initUIGeneraters(self):
        self.__uiGenerators = {}
        self.__uiGenerators[PUConst.UiTypePanel] = self._createPanelGenerator()
        self.__uiGenerators[PUConst.UiTypeText] = self._createTextGenerator()
        self.__uiGenerators[PUConst.UiTypeSeparator] = self._createSeparatorGenerator()
        self.__uiGenerators[PUConst.UiTypeSpinPage] = self._createSpinPageGenerator()
        self.__uiGenerators[PUConst.UiTypeLayoutHorizontal] = self._createLayoutHorizontalGenerator()
        self.__uiGenerators[PUConst.UiTypeLayoutVertical] = self._createLayoutVerticalGenerator()
        self.__uiGenerators[PUConst.UiTypeLayoutGrid] = self._createLayoutGridGenerator()
        self.__uiGenerators[PUConst.UiTypeInput] = self._createInputGenerator()
        self.__uiGenerators[PUConst.UiTypePassword] = self._createPasswordGenerator()
        self.__uiGenerators[PUConst.UiTypeButton] = self._createButtonGenerator()
        self.__uiGenerators[PUConst.UiTypeLink] = self._createLinkGenerator()
        self.__uiGenerators[PUConst.UiTypeTab] = self._createTabGenerator()
        self.__uiGenerators[PUConst.UiTypeTable] = self._createTableGenerator()
        self.__uiGenerators[PUConst.UiTypeDatetime] = self._createDatetimeGenerator()
        self.__uiGenerators[PUConst.UiTypeComboBox] = self._createComboBoxGenerator()
        self.__uiGenerators[PUConst.UiTypeComboList] = self._createComboListGenerator()
        self.__uiGenerators[PUConst.UiTypeMap] = self._createMapGenerator()

    def __getUIGenerator(self, uiType):
        return self.__uiGenerators[uiType]

    def __clone(self, uiConfig):
        copyConfig = copy.deepcopy(uiConfig)
        hiddenUIDs = {}
        generatedUIDs = {}
        for key in copyConfig:
            self.__doClone(copyConfig, copyConfig[key], hiddenUIDs, generatedUIDs)
        for key in hiddenUIDs:
            copyConfig[key] = hiddenUIDs[key]
        for key in generatedUIDs:
            copyConfig[key] = generatedUIDs[key]
            copyConfig[key][PUConst.UiPropID] = key
        return copyConfig

    # Copy uid hidden deep in sub UIs to top level dict
    # So the uid could be got directly
    # If there is no uid for ui element, generate one
    def __doClone(self, uiConfig, uiElement, hiddenUIDs, generatedUIDs):
        if isinstance(uiElement, dict):
            if PUConst.UiPropSubUI not in uiElement:
                return
            subUIs = uiElement[PUConst.UiPropSubUI]
            if isinstance(subUIs, list):
                self.__cloneList(uiConfig, subUIs, hiddenUIDs, generatedUIDs)
            if isinstance(subUIs, dict):
                for key in subUIs:
                    if key in uiConfig or key in hiddenUIDs:
                        logger.error(f"Duplicated uid: {key}")
                        exit(f"Duplicated uid: {key}")
                    else:
                        hiddenUIDs[key] = subUIs[key]
                    self.__doClone(uiConfig, subUIs[key], hiddenUIDs, generatedUIDs)
        if isinstance(uiElement, list):
            self.__cloneList(uiConfig, uiElement, hiddenUIDs, generatedUIDs)

    def __cloneList(self, uiConfig, uiList, hiddenUIDs, generatedUIDs):
        for value in uiList:
            if not isinstance(value, dict):
                continue
            if PUConst.UiPropID in value:
                uid = value[PUConst.UiPropID]
                if uid in uiConfig or uid in hiddenUIDs:
                    logger.error(f"Duplicated uid in list: {uid}")
                    exit(f"Duplicated uid in list: {uid}")
                else:
                    hiddenUIDs[uid] = value
            else:
                uid = self.__generateUID()
                generatedUIDs[uid] = value
            self.__doClone(uiConfig, value, hiddenUIDs, generatedUIDs)

    def __generateUID(self):
        self.__generatedId += 1
        return "uidGenerated" + str(self.__generatedId)
